﻿using System;
namespace ElementaryMath
{
    public static class Constants
    {

        public static double Factorial(int n)
        {
            if (n == 0)
            {
                return 1;
            }
            return n * Factorial(n - 1);
        }

    }


    public abstract class Function
    {
        // 求F(x)
        public abstract double Calculate(double x);
        // 求F(x)的n阶导函数
        public abstract Function DerivativeFunction(int n);
        public abstract override string ToString();
        // 初步简化 f(x)=(x)+((x)*3)-->f(x)=x+x*3
        public Function simaplify()
        {
            Function func = this.simaplifyOnce();
            if(func == null)
            { 
                return this;
            }
            return func.simaplify();
        }

        public abstract Function simaplifyOnce();
    }


    public class X : Function
    {
        public override double Calculate(double x)
        {
            return x;
        }
        public override Function DerivativeFunction(int n)
        {
            if (n == 1)
            {
                return new Constant(1);
            }
            else
            {
                return new Constant(0);
            }
        }

        public override Function simaplifyOnce()
        {
            return null;
        }

        public override string ToString()
        {
            return "x";
        }
    }


    public class Sin : Function
    {
        private Function func;
        // f(x) = sin(func(x))
        public Sin(Function func)
        {
            this.func = func;
        }

        public override double Calculate(double x)
        {
            return Math.Sin(func.Calculate(x));
        }
        public override Function DerivativeFunction(int n)
        {
            Function One = new Mul(new Cos(this.func), this.func.DerivativeFunction(1));
            if (n == 1)
            {
                return One;
            }
            return One.DerivativeFunction(n - 1);
        }
        public override Function simaplifyOnce()
        {
            Function func = this.func.simaplifyOnce();
            if(func == null)
            {
                return null;
            }
            return new Sin(func);

        }

        public override string ToString()
        {
            return "sin(" + this.func.ToString() + ")";
        }
    }

    public class Cos : Function
    {
        private Function func;
        public Cos(Function func)
        {
            this.func = func;
        }
        public override double Calculate(double x)
        {
            return Math.Cos(this.func.Calculate(x));
        }
        public override Function DerivativeFunction(int n)
        {
            Function One = new Mul(new Mul(new Constant(-1), new Sin(this.func)), this.func.DerivativeFunction(1));
            if (n == 1)
            {
                return One;
            }
            return One.DerivativeFunction(n - 1);
        }

        public override string ToString()
        {
            return "cos(" + this.func.ToString() + ")";
        }

        public override Function simaplifyOnce()
        {
            Function func = this.func.simaplifyOnce();
            if (func == null)
            {
                return null;
            }
            return new Cos(func);
        }
    }


    public class Pow : Function

    {
        private Function func;
        private double n;
        // f(x) = [func(x)]^n
        public Pow(Function func, double n)
        {
            this.func = func;
            this.n = n;
        }
        //  f(x) = [func(x)]^n
        public override double Calculate(double x)
        {
            return Math.Pow(this.func.Calculate(x), this.n);
        }
        public override Function DerivativeFunction(int n)
        {
            Function one = new Mul(new Mul(new Constant(this.n), new Pow(this.func, this.n-1)), this.func.DerivativeFunction(1));
            if (n == 1)
            {
                return one;
            }
            return one.DerivativeFunction(n - 1);

        }

        public override Function simaplifyOnce()
        {
            if (n == 1)
            {
                Function func = this.func.simaplifyOnce();
                if (func == null)
                {
                    return this.func;
                }
                return func;
            }
            else if (n == 0)
            {
                return new Constant(1);
            }
            else
            {
                Function func = this.func.simaplifyOnce();
                if (func == null)
                {
                    return null;
                }

                return new Pow(func, this.n);
            }

        }

        public override string ToString()
        {
            return "(" + this.func.ToString() + ")^" + this.n.ToString();
        }
    }

    
    public class Constant : Function
    {
        // 对应的常数
        private double n;

        public Constant(double n)
        {
            this.n = n;
        }
        // f(x) = n
        public override double Calculate(double x)
        {
            return n;
        }
        // f'(x) = 0
        public override Function DerivativeFunction(int n)
        {
            return new Constant(0);
        }

        public override string ToString()
        {
            return n.ToString();
        }
 
        public override Function simaplifyOnce()
        {
            return null;
        }
    }


    public class Add : Function
    {
        // func1 + func2 
        private Function func1;
        private Function func2;

        public Add(Function func1, Function func2)
        {
            this.func1 = func1;
            this.func2 = func2;
        }

        // f(x) = func1(x) + func2(x)
        public override double Calculate(double x)
        {
            return this.func1.Calculate(x) + this.func2.Calculate(x);
        }
        // 求n阶导函数
        public override Function DerivativeFunction(int n)
        {
            Function one = new Add(func1.DerivativeFunction(1), this.func2.DerivativeFunction(1));
            if (n == 1)
            {
                return one;
            }
            return one.DerivativeFunction(n - 1);
        }

        // 优化计算
        public override Function simaplifyOnce()
        {
            if (this.func1 is Constant && this.func2 is Constant)
            {
                return new Constant(this.func1.Calculate(0) + this.func2.Calculate(0));
            }
            else if (this.func1 is Constant && this.func1.Calculate(0) == 0)
            {
                return this.func2;
            }
            else if (this.func2 is Constant && this.func2.Calculate(0) == 0)
            {
                return this.func1;
            }
            else
            {
                Function func = this.func1.simaplifyOnce();
                if (func != null)
                {
                    return new Add(func, this.func2);
                }
                func = this.func2.simaplifyOnce();
                if (func != null)
                {
                    return new Add(this.func1, func);
                }
                return null;
            }
        }

        public override string ToString()
        {
            return "(" + this.func1.ToString() + " + " + this.func2.ToString() + ")";
        }
    }

    public class Sub : Function
    {
        private Function func1;
        private Function func2;
        public Sub(Function func1, Function func2)
        {
            this.func1 = func1;
            this.func2 = func2;
        }
        // f(x) = func1(x)-func2(x)
        public override double Calculate(double x)
        {
            return this.func1.Calculate(x) - this.func2.Calculate(x);

        }
        // f^(n)(x) = func1^(n)(x) - func2^(n)(x)
        public override Function DerivativeFunction(int n)
        {
            return new Sub(this.func1.DerivativeFunction(n), this.func2.DerivativeFunction(n));
        }

        public override Function simaplifyOnce()
        {
            if (this.func1 is Constant && this.func2 is Constant)
            {
                return new Constant(this.func1.Calculate(0) - this.func2.Calculate(0));
            }
            else if(this.func1 is Constant && this.func1.Calculate(0) == 0)
            {
                return new Mul(new Constant(-1), this.func2);
            }
            else if(this.func2 is Constant && this.func2.Calculate(0) == 0)
            {
                return this.func1;
            }
            else
            {
                Function func = this.func1.simaplifyOnce();
                if (func != null)
                {
                    return new Sub(func, this.func2);
                }
                func = this.func2.simaplifyOnce();
                if (func != null)
                {
                    return new Sub(this.func1, func);
                }
                return null;
            }
        }

        public override string ToString()
        {
            return "(" + this.func1.ToString() + " - " + this.func2.ToString() + ")";
        }
    }


    public class Mul : Function
    {
        private Function func1;
        private Function func2;
        public Mul(Function func1, Function func2)
        {
            this.func1 = func1;
            this.func2 = func2;
        }
        // f(x) = func1(x) * func2(x)
        public override double Calculate(double x)
        {
            return this.func1.Calculate(x) * this.func2.Calculate(x);
        }
        // f'(x) = func1'(x)*func2(x) + func2'(x)*func1(x)
        // f^(n)(x) = [ (f^(n-1))(x) ]'
        public override Function DerivativeFunction(int n)
        {
            Function one = new Add(new Mul(this.func1.DerivativeFunction(1), this.func2), new Mul(this.func1, this.func2.DerivativeFunction(1)));
            if (n == 1)
            {
                return one;
            }
            return one.DerivativeFunction(n - 1);
        }

        public override Function simaplifyOnce()
        {
            if (this.func1 is Constant && this.func2 is Constant)
            {
                return new Constant(this.func1.Calculate(0) * this.func2.Calculate(0));
            }
            else if (this.func1 is Constant && this.func1.Calculate(0) == 0)
            {
                return new Constant(0);
            }
            else if (this.func2 is Constant && this.func2.Calculate(0) == 0)
            {
                return new Constant(0);
            }
            else if(this.func1 is Constant && this.func1.Calculate(0)==1)
            {
                 Function func = this.func2.simaplifyOnce();
                if (func != null)
                {
                    return func;
                }
                return this.func2;
            }
            else if (this.func2 is Constant && this.func2.Calculate(0) == 1)
            {
                Function func = this.func1.simaplifyOnce();
                if (func != null)
                {
                    return func;
                }
                return this.func1;
            }
            else
            {
                Function func = this.func1.simaplifyOnce();
                if (func != null)
                {
                    return new Mul(func, this.func2);
                }
                func = this.func2.simaplifyOnce();
                if (func != null)
                {
                    return new Mul(this.func1, func);
                }
                return null;
            }
        }

        public override string ToString()
        {
            return "(" + this.func1.ToString() + " * " + this.func2.ToString() + ")";
        }
    }

    public class Div : Function
    {
        private Function func1;
        private Function func2;
        public Div(Function func1, Function func2)
        {
            this.func1 = func1;
            this.func2 = func2;
        }
        public override double Calculate(double x)
        {
            return this.func1.Calculate(x) / this.func2.Calculate(x);
        }
        public override Function DerivativeFunction(int n)
        {
            Function one = new Div(new Sub(new Mul(this.func1.DerivativeFunction(n), this.func2), new Mul(this.func1, this.func2.DerivativeFunction(n))), new Pow(this.func2, 2));
            if (n == 1)
            {
                return one;
            }
            return one.DerivativeFunction(n - 1);
        }

        public override Function simaplifyOnce()
        {
            if (this.func1 is Constant && this.func1.Calculate(0) == 0)
            {
                return new Constant(0);
            }
            else if (this.func2 is Constant && this.func2.Calculate(0) == 1)
            {
                return this.func1;
            }
            else
            {
                Function func = this.func1.simaplifyOnce();
                if (func != null)
                {
                    return new Div(func, this.func2);
                }
                func = this.func2.simaplifyOnce();
                if (func != null)
                {
                    return new Div(this.func1, func);
                }
                return null;
            }
               
        }

        public override string ToString()
        {
            return "(" + this.func1.ToString() + " / " + this.func2.ToString() + ")";
        }
    }

    //public class ExponentiatedNum : Function
    //{
    //    private Function func;
    //    private double n;

    //    public ExponentiatedNum(Function func, double n)
    //    {
    //        this.func = func;
    //        this.n = n;
    //    }
    //    public override double Calculate(double x)
    //    {
    //        return Math.Pow(this.n, this.func.Calculate(x));
    //    }

    //    public override Function DerivativeFunction(int n)
    //    {
    //        Function one = new Mul(new Mul(new ExponentiatedNum(this.func, this.n), new Log(new Constant(this.n), Math.E)), this.func.DerivativeFunction(1));
    //        if (n == 1)
    //        {
    //            return one;
    //        }
    //        return one.DerivativeFunction(n - 1);
    //    }

    //    public override Function simaplifyOnce()
    //    {
    //        if (n == 0)
    //        {
    //            return new Constant(0);
    //        }
    //        else if (n == 1)
    //        {
    //            return new Constant(1);
    //        }
    //        else if (this.func is Constant)
    //        {
    //            return new Constant(Math.Pow(this.n, this.func.Calculate(0)));
    //        }
    //        else
    //        {
    //            return new ExponentiatedNum(this.func.simaplify(), this.n);
    //        }
    //    }

    //    public override string ToString()
    //    {
    //        return this.n + "^{" + this.func.ToString() + "}";
    //    }
    //}



    //public class Log : Function
    //{
    //    private Function func;
    //    private double n;

    //    public Log(Function func, double n)
    //    {
    //        this.func = func;
    //        this.n = n;
    //    }
    //    public override double Calculate(double x)
    //    {
    //        return Math.Log(this.func.Calculate(x), this.n);
    //    }

    //    public override Function DerivativeFunction(int n)
    //    {
    //        Function one = new Mul(new Div(new Constant(1), new Mul(new X(), new Log(new Constant(this.n), Math.E))), this.func.DerivativeFunction(1));
    //        if (n == 1)
    //        {
    //            return one;
    //        }
    //        return one.DerivativeFunction(n - 1);
    //    }

    //    public override Function simaplify()
    //    {
    //        if (this.func is Constant)
    //        {
    //            return new Constant(Math.Log(this.func.Calculate(0), this.n));
    //        }
    //        else if (n == 1)
    //        {
    //            return new Constant(0);
    //        }
    //        else
    //        {
    //            return new Log(this.func.simaplify(), this.n);
    //        }
    //    }

    //    public override string ToString()
    //    {
    //        return "log_{" + this.n + "}(" + this.func.ToString() + ")";
    //    }
    //}

}